using System;
using System.Xml;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using Microsoft.Health.PatientConnect;
using Microsoft.Health.ItemTypes;
using System.IO;
using Microsoft.Health.Web;
using System.Xml.XPath;
namespace Microsoft.Health.Samples.PatientConnect.TreyResearch.HealthVaultAdapter
{
    /// <summary>
    /// Class handling Inbound operations (Reading information from Microsoft HealthVault)
    /// </summary>
    public class ReadHealthVaultItemTypes
    {
        private Guid _applicationId = new Guid();

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="appId">ApplicationID</param>
        public ReadHealthVaultItemTypes(Guid appId)
        {
            _applicationId = appId;
        }

        /// <summary>
        /// Get all the thing types registered with Health vault
        /// </summary>
        public Collection<HealthVaultThingTypes> GetAllThingType
        {
            get
            {
                Collection<HealthVaultThingTypes> healthVaultThingTypesList = new Collection<HealthVaultThingTypes>();
                HealthVaultThingTypes thingtypes = new HealthVaultThingTypes();
                ApplicationConnection appconn = new ApplicationConnection(WebApplicationConfiguration.AppId, WebApplicationConfiguration.HealthServiceUrl);
                HealthRecordItemTypeDefinition baseTypes = ItemTypeManager.GetBaseHealthRecordItemTypeDefinition(appconn);
                Vocabulary vocabularythingTypes = appconn.GetVocabulary("thing-types");
                foreach (KeyValuePair<string, VocabularyItem> item in vocabularythingTypes)
                {
                    thingtypes = new HealthVaultThingTypes();
                    thingtypes.ItemName = item.Value.ToString();
                    thingtypes.TypeId = new Guid(item.Key);
                    healthVaultThingTypesList.Add(thingtypes);
                }
                return healthVaultThingTypesList;
            }
        }

        /// <summary>
        /// Checks whether Guid is valid.
        /// </summary>
        /// <param name="patientId">PatientId to be validated</param>
        /// <returns>True if the PatientId is a valid Guid else returns false.</returns>
        private bool IsValidGuid(string patientId)
        {
            Guid patientGuid = Guid.Empty;
            bool isValid = false;

            try
            {
                patientGuid = new Guid(patientId);
                isValid = true;
            }
            catch //suppressing the error during GUID validation
            {
            }
            return isValid;
        }

        /// <summary>
        /// Fetch medication details from health vault
        /// </summary>
        /// <param name="personId">Person Id</param>
        /// <param name="recordId">Record Id</param>
        /// <returns>collection of XmlDocument for the XML</returns>
        private Collection<XmlDocument> GetPatientMedication(Guid personId,
                                                             Guid recordId)
        {
            HealthRecordAccessor offlineAccessor = new HealthRecordAccessor(HealthVaultConnectionManager.CreateConnection(_applicationId, personId), recordId);
            Collection<Medication> medicationCollection = GetHealthRecordItemCollection<Medication>(Medication.TypeId, offlineAccessor);
            Collection<XmlDocument> xmlCollection = new Collection<XmlDocument>();
            XmlDocument medicationItem;
            foreach (Medication medication in medicationCollection)
            {
                medicationItem = new XmlDocument();
                medicationItem.LoadXml(medication.GetItemXml());
                xmlCollection.Add(medicationItem);
            }
            return xmlCollection;
        }

        /// <summary>
        /// Gets the collection of Health Record Item
        /// </summary>
        /// <typeparam name="T">Generic type</typeparam>
        /// <param name="typeID">HealthRecordItem TypeId</param>
        /// <param name="offlineAccessor">Reference to HealthRecordAccessor</param>
        /// <returns>Collection of Health Record Item</returns>
        Collection<T> GetHealthRecordItemCollection<T>(Guid typeID,
                                                       HealthRecordAccessor offlineAccessor) where T : HealthRecordItem
        {

            HealthRecordSearcher offlineSearcher = new HealthRecordSearcher(offlineAccessor);
            offlineSearcher.Filters.Add(new HealthRecordFilter(typeID));
            HealthRecordItemCollection offlineItems = offlineSearcher.GetMatchingItems()[0];
            Collection<T> recordCollection = new Collection<T>();
            foreach (HealthRecordItem item in offlineItems)
            {
                recordCollection.Add((T)item);
            }
            return recordCollection;
        }

        /// <summary>
        /// Gets the validated patient and write to the local xml file
        /// </summary>
        /// <param name="xmlFilePath"></param>
        /// <param name="patientValidationTime"></param>
        public void GetPatients(string xmlFilePath,
                                DateTime patientValidationTime)
        {
            OfflineWebApplicationConnection offlineConnection = HealthVaultConnectionManager.CreateConnection(_applicationId);
            Collection<ValidatedPatientConnection> validatedConnection = PatientConnection.GetValidatedConnections(offlineConnection, patientValidationTime);
            foreach (ValidatedPatientConnection validatedPatient in validatedConnection)
            {
                Patient patient = new Patient(xmlFilePath);
                //Check whether the patient is registered with this provider
                List<Patient> Registeredpatient = patient.SelectPatientRecords("id='" + validatedPatient.ApplicationPatientId + "'");

                //Checks for valid patient id and adds the record
                if (IsValidGuid(validatedPatient.ApplicationPatientId) && Registeredpatient.Count > 0)
                {
                    patient.PatientId = new Guid(validatedPatient.ApplicationPatientId);
                    patient.PersonId = validatedPatient.PersonId;
                    patient.RecordId = validatedPatient.RecordId;
                    HealthRecordAccessor accessor = new HealthRecordAccessor(HealthVaultConnectionManager.CreateConnection(_applicationId, patient.PersonId), patient.RecordId);
                    Basic basicinformation = GetHealthRecordItemValue<Basic>
                        (Health.ItemTypes.Basic.TypeId, accessor);

                    patient.YearOfBirth = basicinformation.BirthYear;
                    patient.Country = basicinformation.Country;
                    patient.City = basicinformation.City;
                    Gender gender = (Gender)basicinformation.Gender;
                    patient.Gender = gender.ToString();
                    patient.State = basicinformation.StateOrProvince;
                    patient.RegisteredWithHealthVault = true;
                    patient.UpdatePatient("id = '" + patient.PatientId.ToString() + "'");

                    //Checks whether the file path is complete
                    if (Path.HasExtension(xmlFilePath))
                    {
                        GetPersonalImage(patient.PersonId, patient.RecordId, Path.GetDirectoryName(xmlFilePath));
                    }

                    //Get Medication
                    Collection<XmlDocument> medications = GetPatientMedication(patient.PersonId, patient.RecordId);
                    if (medications.Count > 0)
                    {
                        string medicationFilePath = string.Format(@"{0}\Medications", Path.GetDirectoryName(xmlFilePath));
                        DirectoryInfo dirInfo = new DirectoryInfo(medicationFilePath.ToString());
                        FileInfo[] fileInfo = dirInfo.GetFiles("*.xml");
                        foreach (FileInfo medicationFileInfo in fileInfo)
                        {
                            if (medicationFileInfo.Name.Contains(patient.PatientId.ToString()))
                            {
                                UpdateMedicationXml(medicationFileInfo.FullName, medications);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the Basic Demographic information of the patient
        /// </summary>
        /// <param name="patientId">Patient Id as in Local database</param>
        /// <param name="personId">Person Id</param>
        /// <param name="recordId">Authorized record id</param>
        /// <param name="xmlFilePath">Xml file path</param>
        private void GetBasicInformation(Guid patientId,
                                         Guid personId,
                                         Guid recordId,
                                         string xmlFilePath)
        {
            Patient patient = new Patient(xmlFilePath);
            HealthRecordAccessor accessor = new HealthRecordAccessor(HealthVaultConnectionManager.CreateConnection(_applicationId, personId), recordId);
            Basic basicinformation = GetHealthRecordItemValue<Basic>
                (Health.ItemTypes.Basic.TypeId, accessor);
            patient.PatientId = patientId;
            patient.PersonId = personId;
            patient.RecordId = recordId;
            patient.YearOfBirth = basicinformation.BirthYear;
            patient.Country = basicinformation.Country;
            patient.City = basicinformation.City;
            Gender gender = (Gender)basicinformation.Gender;
            patient.Gender = gender.ToString();
            patient.State = basicinformation.StateOrProvince;
            patient.UpdatePatient("id = '" + patientId.ToString() + "'");
        }

        /// <summary>
        /// Fetch person's image from health vault(Image which is uploaded by the user in its profile)
        /// </summary>
        /// <param name="personId">PersonId</param>
        /// <param name="recordId">RecordId</param>
        /// <param name="imageStorePath">Path for storing image in local disk</param>
        private void GetPersonalImage(Guid personId,
                                     Guid recordId,
                                     string imageStorePath)
        {
            HealthRecordAccessor accessor = new HealthRecordAccessor(HealthVaultConnectionManager.CreateConnection(_applicationId, personId), recordId);
            HealthRecordItemCollection imageCollection = accessor.GetItemsByType(PersonalImage.TypeId, HealthRecordItemSections.All);
            if (imageCollection.Count > 0)
            {
                PersonalImage personalimage = new PersonalImage();
                personalimage = imageCollection[0] as PersonalImage;
                if (personalimage != null)
                {
                    using (Stream currentImageStream = personalimage.ReadImage())
                    {
                        byte[] imageBytes = new byte[currentImageStream.Length];
                        currentImageStream.Read(imageBytes, 0, (int)currentImageStream.Length);
                        using (FileStream outputImage = System.IO.File.OpenWrite(Path.Combine(imageStorePath, recordId + ".jpg")))
                        {
                            outputImage.Write(imageBytes, 0, imageBytes.Length);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets HealthRecordItem for the given type id
        /// </summary>
        /// <typeparam name="T">Type of object</typeparam>
        /// <param name="typeID">Type Id for health vault record item</param>
        /// <param name="accessor">Reference to health record accessor</param>
        /// <returns>Health Record Item</returns>
        T GetHealthRecordItemValue<T>(Guid typeID,
                                      HealthRecordAccessor accessor) where T : class
        {
            HealthRecordSearcher searcher = new HealthRecordSearcher(accessor);
            searcher.Filters.Add(new HealthRecordFilter(typeID));
            HealthRecordItemCollection items = searcher.GetMatchingItems()[0];
            if (items != null && items.Count > 0)
            {
                return items[0] as T;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Updates Medication xml file
        /// </summary>
        /// <param name="medicationFilePath">Medication folder path</param>
        /// <param name="xmlDocCollection">Collection of xml document</param>
        private void UpdateMedicationXml(string medicationFilePath,
                                         Collection<XmlDocument> xmlDocCollection)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(medicationFilePath);
            XPathNavigator navigator = xmlDoc.CreateNavigator();
            XPathNodeIterator nodeIterator = navigator.Select("/Medications");
            nodeIterator.MoveNext();
            string medicationsDetails = string.Empty;
            foreach (XmlDocument medicationxmldoc in xmlDocCollection)
            {
                medicationsDetails = string.Concat(medicationsDetails, medicationxmldoc.InnerXml);
            }
            nodeIterator.Current.InnerXml = medicationsDetails.ToString();
            xmlDoc.Save(medicationFilePath);
        }
    }
}